/**
 * DragonFly is yet another javascript helper library designed speicfically to
 * work well with the SharkTooth Communication library and the FreeRaven Templating library
 *
 * This library was inspired by and intentionally modelled after the
 * functionality of both prototype.js and jquery.js. In a sense it is a 'hybrid'
 * of both librariers that utlizes the best features seen in both librariers.
 *  It also provides a very robust CSS3 Selector Enginer
 *
 * @author Rick Allen <stoodder@gmail.com>

 TODO:
 NEED TO MAKE THE CLEAR CACHE METHOD WORK! Piority #1

NEED TO OPTIMIZE MEMORY USAGE, especially within the Selector

 Object
 -----------------------
 keys() ?
 values() ?
 -----------------------

 Document ? may replace Window with this
 -----------------------
 dimensions()
 scroll()
 -----------------------

 Element
 -----------------------
 extendArray() //extend the Array prototype with methods

 empty() //is it empty

 add() //adds a specified element
 remove() //removes this element

 height()
 width()
 outerHeight()?
 outerWidth()?
 innerHeight()?
 innerWidth()?
 dimensions()?
 top()
 left()
 offset()?

 absolutize()
 relativize()

 id()
 index() //this element's index under its parent node

 clone()

 val()

 text()

 observe()
 unobserve()

 nextAll() //get all next siblings
 previousAll() //get all previous siblings
 -----------------------


 - Why do we need to use Object.prototype.each.call when getting an elements offspring?
 - Find an easier way to identify Element
 - Ajax? may not need to do this here
 - Function Support
 - Extend everything to use funciton support
 - Events
 */

/**
 * First start the DragonFly object and any necessary script setup
 */
var DragonFly = {
    version : '1.0.4',
    transceiver: function (input) {
        return input;
    },
    emptyFunction: function () {},
    duff: function (iterations, method, context) {
        try {
	  if (iterations == 0) return;

	  context = context || this;

	  switch (iterations % 8) {
	      case 0: method.call(context);
	      case 7: method.call(context);
	      case 6: method.call(context);
	      case 5: method.call(context);
	      case 4: method.call(context);
	      case 3: method.call(context);
	      case 2: method.call(context);
	      case 1: method.call(context);
	  }

	  var n = parseInt( --iterations / 8);

	  while (--n >= 0) {
	      method.call(context); //0
	      method.call(context); //1
	      method.call(context); //2
	      method.call(context); //3
	      method.call(context); //4
	      method.call(context); //5
	      method.call(context); //6
	      method.call(context); //7
	  }
        } catch (e) {
	  if (e != DragonFly.$break) throw e;
        }
    },
    $break: function () {
        throw DragonFly.$break;
    }
};

//make certain methods global
var $break = DragonFly.$break;

/**
* Define operations specific to the browser window.
*/
var Window = {
    /**
	* Method used to get the height of the window if found
	* This specifically referes to getting the inner height of the window;
	* The actual web content itself
	*
	* @return {Number} The height of the window
	*/
    height : function() {
        return (
	  !!window.innerHeight ?
	  window.innerHeight : (
	      !!document ? (
		!!document.documentElement ?
		document.documentElement.offsetHeight :
		document.body.offsetHeight
		) : 0
	      )
	  );
    },

    /**
	* Method used to get the width of the window if found
	* This specifically referes to getting the inner width of the window;
	* The actual web content itself
	*
	* @return {Number} The width of the window
	*/
    width : function() {
        return (
	  !!window.innerWidth ?
	  window.innerWidth : (
	      !!document ? (
		!!document.documentElement ?
		document.documentElement.offsetWidth :
		document.body.offsetWidth
		) : 0
	      )
	  );
    },

    /**
	* Simple method to get both the height and the width of the window
	* This specifically referes to getting the inner dimensions of the window;
	* The actual web content itself
	*
	* @return {Object} The height and width of the window
	*/
    dimensions : function()
    {
        return {
	  height : this.height(),
	  width : this.width()
        };
    },

    /**
	* Scroll to a specific x coordinate on the window
	*
	* @param {Number|null} x The x coordinate to scroll to
	*
	* @return {Number|Window} Returns either the current scroll x coordinate or the Window after scrolling to the given x coordinate
	*/
    scrollX : function( x )
    {
        if( typeof x == 'number' && x >= 0 )
        {
	  window.scrollTo( x, Window.scrollY() );
	  return this;
        }

        return (
	  !!window.pageXOffset ?
	  window.pageXOffset : (
	      !!document ?
	      document.body.scrollLeft :
	      0
	      )
	  );
    },

    /**
	* Scroll to a specific y coordinate on the window
	*
	* @param {Number|null} y The y coordinate to scroll to
	*
	* @return {Number|Window} Returns either the current scroll y coordinate or the Window after scrolling to the given y coordinate
	*/
    scrollY : function( y )
    {
        if( typeof y == 'number' && y >= 0 )
        {
	  window.scrollTo( Window.scrollX(), y );
	  return this;
        }

        return (
	  !!window.pageYOffset ?
	  window.pageYOffset : (
	      !!document ?
	      document.body.scrollTop :
	      0
	      )
	  );
    },

    /**
	* Method used to either scroll to an x, y position or to return both the xurrent x,y scroll position
	*
	* @param {Number} x The x coordinate to scroll to
	* @param {Number} y The y coordinate to scroll to
	*
	* @return {Window|Object} Returns either the window or an object with x,y coordinates
	*/
    scroll : function( x, y )
    {
        //if neither is defined, simply return the current scroll ofset values
        if( typeof x != 'number' && typeof y != 'number' )
        {
	  return {
	      x : this.scrollX(),
	      y : this.scrollY()
	  };
        }

        x = ( typeof x == 'number' && x >= 0 ? x : Window.scrollX() );
        y = ( typeof y == 'number' && y >= 0 ? y : Window.scrollY() );

        //otherwise attempt to school tho these values
        window.scrollTo(x, y);

        //return this object
        return this;
    }
};

/**
 * Beginning funtionality to help use extend the Object object to add boolean
 * methods for type checking.
 */
(function () {
    /**
     * Method used to see if a given object is an Object
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this an Object?
     */
    Object.isObject = function (object) {
        return ( typeof object == typeof {} );
    };

    /**
     * Method used to see if a given object is an Array
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this an Array?
     */
    Object.isArray = function (object) {
        return ( typeof object == typeof [] );
    };

    /**
     * Method used to see if a given object is a Function
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this a Function?
     */
    Object.isFunction = function (object) {
        return ( typeof object == typeof function(){} );
    };

    /**
     * Method used to see if a given object is a String
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this a String?
     */
    Object.isString = function (object) {
        return ( typeof object == typeof "" );
    };

    /**
     * Method used to see if a given object is a Number
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this a Number?
     */
    Object.isNumber = function (object) {
        return ( typeof object == typeof 1 );
    };

    /**
     * Method used to see if a given object is a Boolean
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this a Boolean?
     */
    Object.isBoolean = function (object) {
        return ( typeof object == typeof true );
    };

    /**
     * Method used to see if a given object is a RegExp
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this a Regular Expression?
     */
    Object.isRegExp = function (object) {
        return ( typeof object == typeof /./ );
    };

    /**
     * Method used to see if a given object is undefined
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this undefined?
     */
    Object.isUndefined = function (object) {
        return (typeof object == typeof undefined);
    };

    /**
     * Method used to see if a given object is null
     *
     * @param {mixed} object The object to check
     *
     * @return {Boolean} Was this null?
     */
    Object.isNull = function (object) {
        return (object == null);
    };
})();

/**
 * Extend the core Object functionality
 */
(function () {
    /**
     * Method used to iterate over this object
     *
     * @param {Function} iterator The method to pass each value into
     * @param {Object} context The context for which the 'this' reserved word means in the iterator
     */
    Object.prototype.each = function (iterator, context) {
        try {
	  if ( typeof iterator != 'function' )
	      return;

	  var key;
	  context = context || this;

	  if (iterator.length == 1) {
	      for (key in this) if (!(key in Object.prototype)) iterator.call(context, this[key]);
	  } else {
	      var i = 0;
	      for (key in this) if (!(key in Object.prototype)) iterator.call(context, key, this[key], ++i);
	  }
        } catch (e) {
	  if (e != DragonFly.$break) throw e;
        }
    };

    /**
     * Updates this object with the other object where all values from the
     * other object are copied to this object
     *
     * @param {Object} extend The object to update this one with
     *
     * @return This updated object
     */
    Object.prototype.extend = function (extend) {
        if (typeof extend != 'object')
	  return this;

        extend.each(function (key, value) {
	  this[key] = value;
        }, this);

        return this;
    };

    /**
     * joins each of the elements in this object togethor to form a string representation
     *
     * @param {String} itemSeperator String value to seperate each item
     * @param {String} keyValueSeperator String value to seperate the key from its value
     *
     * @return {String} The string representation of this
     */
    Object.prototype.join = function (keyValueSeperator, itemSeperator) {
        keyValueSeperator = (typeof keyValueSeperator == 'string' ? keyValueSeperator : ":");
        itemSeperator = (typeof itemSeperator == 'string' ? itemSeperator : ",");

        var str = "";

        this.each(function (key, value) {
	  str += key + keyValueSeperator + value + itemSeperator;
        });

        return str.substring(0, str.length - itemSeperator.length);
    };

    /**
     * Method used to return this object as a name-value paired query string
     *
     * @return {String} The name-value paired query string
     */
    Object.prototype.toQueryString = function () {
        return this.join("&", "=");
    };

    /**
	* Method used to make a clone of this object
	* @param {Boolean} deep Should we make a deep copy of this object?
	*
	* @return {Object} The cloned object

	TODO: Test this
	*/
    Object.prototype.clone = function( deep )
    {
        deep = ( typeof deep == 'boolean' ? deep : false );
        var clone = {};

        this.each( function(key, value) {
	  clone[key] =  ( deep && typeof value == 'object' ? value.clone( deep ) : value );
        });

        return clone;
    }

	Object.prototype.array = function()
	{
		array = [],
		length = this.length || 0;

		for( var i = 0; i < length; ++i )
			array[i] = this[i];

		return array;
	}
})();

/**
 * Extending all od the core Array functionality
 */
(function () {
    /**
     * Method used to ierate over the contents of this array
     *
     * @param {Function} iterator The function iterator used to analyze each element
     * @param {Object} context The context for which the 'this' reserved word means in the iterator
     */
    Array.prototype.each = function (iterator, context) {
        if( typeof iterator != 'function' )
	  return;

        context = context || this;

        var i = -1;

        DragonFly.duff(this.length, function () {
	  iterator.call(context, this[++i], i);
        }, this);
    };

    /**
     * Method that will "flatten" the contents of this array
     * converts [ [1,2] , [3,4] ] => [1,2,3,4]
     *
     * @return {Array} The flattened array
     */
    Array.prototype.flatten = function () {
        var flattened = [], i = -1, j;

        (function _flatten(arr) {
	  j = -1;
	  DragonFly.duff(arr.length, function () {
	      ( typeof arr[++j] == 'array' ? _flatten(arr[j]) : (flattened[++i] = arr[j]));
	  });
        })(this);

        return flattened;
    };

    /**
     * Method used to transform each of the elements in this array by the given
     * 'transformer' method.
     *
     * @param {Function} transformer The transformer method to usse
     * @param {Object|null} context The context to associate the method call with
     *
     * @return {Array} The transformed array
     */
    Array.prototype.transform = function (transformer, context) {
        //TODO: cehck if the tansformer is a function

        context = context || this;
        var transformed = [];

        this.each(function (elm, i) {
	  transformed[i] = transformer.call(context, elm);
        });

        return transformed;
    };

    /**
     * Method used to convert this array to an object
     *
     * @param {Function} objectizer A method that should return an array of 2 elements to be
     *                   placed into the output object;
     * @param {Object|null} context The object to associate 'this' with in the objectizer
     *
     * @return The object representation of this array
     */
    Array.prototype.objectize = function (objectizer, context) {
        var object = {};
        objectizer = ( typeof objectizer == 'function' ? objectizer : DragonFly.transceiver);
        context = context || {};

        this.each(function (elm, i) {
	  var objectized = objectizer.call(context, elm);

	  //was an array returned?
	  if ( typeof objectized =='array' && objectized.length == 2 && typeof objectized[0] == 'string' ) {
	      object[objectized[0]] = objectized[1];
	  }
	  //was an object generated?
	  else if ( typeof objectized == 'object' ) {
	      object.update(objectized);
	  }
        });

        return object;
    };

    /**
     * Methods used to present an array without the given element
     *
     * @param {mixed} object The object to remove from this array
     * @param {Number} limit The number of elements to remove
     *
     * @return {Array} The new array without the given element
     */
    Array.prototype.remove = function (object, limit) {
        if (!object) return this;

        limit = typeof limit == 'number' ? limit : this.length;
        var array = [],
        i = -1,
        j = -1;

        DragonFly.duff(this.length, function () {
	  (this[++i] != object ? array[++j] = this[i] : --limit);

	  if (limit <= 0) $break();
        }, this);

        return array;
    };

    /**
     *  Method used to present a unique version of the given array
     *
     *  @return {Array} The unique version of this array
     */
    Array.prototype.unique = function () {
        var unique = [],
        copy = this,
        i = -1;

        while (copy.length > 0) {
	  copy = copy.remove( unique[++i] = copy.pop() );
        }

        return unique;
    };
})();

/**
* Extend onto the Function object
*/
(function() {
    /**
      * Method used to create a partially filled version of the original method,
      * Example:
      * 	part("a","b","c")              => "a b c"
      *	_part = part.partial("x","y");
      * 	_part("c")                     => "x y c"
      * 	_part("d")                     => "x y d"
      *
      * @return {Function} The partially filled method
      */
    Function.prototype.partial = function()
    {
        var $this = this, partialArguments = arguments;
        return function()
        {
	  var args = [], i = -1, j = -1;

	  DragonFly.duff(partialArguments.length, function()
	  {
	      args[++i] = this[++j];
	  }, partialArguments);

	  j = -1;
	  DragonFly.duff(arguments.length, function()
	  {
	      args[++i] = this[++j]
	  }, arguments);

	  return $this.apply($this, args);
        }
    };

    /**
	* Binds the given method to another object.  Once this method is called,
	* 'this' will reference the given object
	*
	* @param {mixed} context The object that 'this' should be associated with
	*
	* @return {Function} The bound function
	*/
    Function.prototype.bind = function(context)
    {
        var $this = this;
        context = context || this;

        return function()
        {
	  return $this.call( context, arguments )
        };
    };

    /**
	* Method used to delay this method for a set amount of time in milliseconds.
	* If no time id given it is simply defered until the page s ready to execute it.
	*
	* @param {Number|null} delay The amount of time in milliseconds to delay for
	*/
    Function.prototype.delay = function( delay )
    {
        setTimeout( function(){
	  this()
        }.bind(this), ( typeof delay == 'number' && delay > 0 ? delay : 0 ) );
    }
})();

/**
 * Extend the regular expression object
 */
(function () {
    /**
     * Method used to escape regular expression characters in a string
     *
     * @param {String} string The unescaped string
     *
     * @return {String} The escaped string
     */
    RegExp.escape = function (string) {
        return ((typeof string == 'string') ? string.replace(/([.+$?^*{[])/g, "\\$1") : "");
    };
})();

/**
 * Extending the core String with string specific functiolnality
 */
(function () {
    /**
     * method used to trim all leading and trailing white space of this string
     *
     * @return {String} The trimmed version of this string
     */
    String.prototype.trim = function () {
        return this.replace(/^\s+|\s+$/g, "");
    };

    /**
     * Method used to convert this string to a char array
     *
     * @return {Array} An array representation of this string
     */
    String.prototype.charArray = function () {
        return this.split("");
    };

    /**
     * Method used to reverse this string
     *
     * @return {String} The backwards version of this string
     */
    String.prototype.reverse = function () {
        var string = '',
        i = this.length;

        DragonFly.duff(this.length, function () {
	  string += this.charAt(--i);
        }, this);

        return string;
    };

    /**
     * Simple private method to 'standardize' the given input string
     *
     * @param {String} string The String to standardize
     *
     * @return {String} The standardized String
     */

    function _standardize(string) {
        return string.trim().replace(/[-_\s]+/, " ")
    }

    /**
     * method used to simply put characters in between each word
     *
     * @param {String} character The character to place between words
     *
     * @return {String} The character filled string transformation
     */
    String.prototype.charIt = function (character) {
        //create the ouptut string
        var str = "";

        //split the standardized string by space
        //then loop over each found occurrence and create the camel cased string
        _standardize(this).split(" ").each(function (substr) {
	  str += substr + character;
        });

        return str.substring(0, str.length - character.length);
    };

    /**
     * Method used to turn this string into a camel case representation
     *
     * @return The camel case representation of this string
     */
    String.prototype.camelCaseIt = function () {
        //create the ouptut string
        var str = "";

        //split the standardized string by space
        //then loop over each found occurrence and create the camel cased string
        _standardize(this).toLowerCase().split(" ").each(function (substr, i) {
	  str += ((i == 0) ? substr : substr.charAt(0).toUpperCase() + substr.substring(1, substr.length).toLowerCase());
        });

        return str;
    };
})();

/**
 * Method used to create a new element with a given tag name and attributes
 * The element is automatically extended with the Element.prototype functionality.
 *
 * @param {String} tagName The type of HTML element to make
 * @param {Object} attributes The attributes to set on the object by default
 *
 * @return {Element} The new element

 TODO: Refine this and make it more powerful! superhero like! :)
 */
function Element(tagName, attributes) {
    if ( typeof tagName != 'string' ) {
        return this;
    }

    var element = document.createElement(tagName.trim());
    attributes = ( typeof attributes == 'object' ? attributes : {});

    attributes.each(function (name, value) {
        element.setAttribute(name, value);
    });

    return Element.extend(element);
}

/**
 * Start extending the base functionlaity of the element
 */
(function () {
    Element._type = 'element';

    /**
     * Method used to check if a given object is an Element
     *
     * @param {mixed} object The object to inspect
     *
     * @return {Boolean} Was the object an Element?
     */
    Object.isElement = function (object) {
        return ( !!object && !!object._type && object._type == Element._type );
    };

    /**
     * Extends the given element with the elements in the object's prototype
     * Generates method where element is passed in as the first argument followed
     * by all arguments passed into each individual method
     *
     * @param {Object} element The element to extend
     *
     * @return {Object} The extended element
     */
    Element.extend = function (element) {
        //if this is already an element, return it.
        if (typeof element._type == 'string' && element._type == Element._type) {
	  return element;
        }

        //extend this element with all of the Element methods
        Object.prototype.each.call(Element.prototype, function (key, func) {
	  if ( typeof func == 'function' ) {
	      element[key] = function () {
		var args = [element];

		for (var i = 0; i < arguments.length; i++) {
		    args.push(arguments[i]);
		}

		return func.apply(element, args);
	      }
	  }
        });
        element._type = Element._type;

        return element;
    };

    /**
     * Method to both get and set a singnle or set of attributes on the given
     * element.
     * @param {Element}element The element to use
     * @param {String|Object} attribute The attribute to get or set
     * @param {String} value The value to store at this attribute
     *
     * @return {String|Element} Either the value of the attribute or this element
     */
    Element.prototype.attr = function (element, attribute, value) {
        element = $(element);

        //if the attribute parameter was an object, then they may be assigning multiple values
        if (typeof attribute === 'string') {
	  if (typeof value === 'string') {
	      (attribute != 'class' ? element.setAttribute(attribute, value, false) : element.className = value);
	  } else {
	      return element.getAttribute(attribute);
	  }
        } else if (typeof attribute == 'object') {
	  attribute.each(element.attr);
        }

        return element;
    };

    /**
     * Function used to set or get the styles within a given element.
     *
     * @param {Element} element The element to use
     * @param {String|Object} style The style to get or set
     * @param {String} value The value to store at this style
     *
     * @return {String|Element} Either the value of the style or this element
     */
    Element.prototype.css = function (element, style, value) {
        element = $(element);

        //are they trying to set more then one
        if (typeof style == 'object') {
	  style.each(element.css);
        } else if (typeof style == 'string') {
	  style = style.camelCaseIt();
	  if (typeof value == 'string') {
	      element.style[style] = value;
	  } else {
	      return element.style[style];
	  }
        }
        return element;
    };

	/**
	* Method used to set or get the html within the given element
	*
	* @param {Element} element The element to get/set html
	* @param {String|Function|null} html	The html to set within the element may also be a
	*										function which will be given then html and expects
	*										to receive a string returned of the new html.
	*
		TODO: The cache clearing fails in IE
	*/
    Element.prototype.html = function (element, html) {
        element = $(element);

        if (!html) return element.innerHTML;
        else if (typeof html == 'function') element.innerHTML = html.call(element, element.innerHTML);
        else if (typeof html == 'string') element.innerHTML = html;
        else return element;

        //TODO: clear the cache
        return element;
    };

	/**
	* Method used to determine if this element is empty or not
	*
	* @return {Boolean} Is this empty?
	*/
	Element.prototype.empty = function( element )
	{
		return( $(element).innerHTML.trim().length == 0 );
	};

    /**
     * Method used to hide an individual element
     *
     * @param {Element} element The element to hide
     *
     * @return {Element} The hidden element
     */
    Element.prototype.hide = function (element) {
        element = $(element);

        element.style.display = 'none';

        return element;
    };

    /**
     * Method used to show an individual element
     *
     * @param {Element} element The element to show
     *
     * @return {Element} The visible element
     */
    Element.prototype.show = function (element) {
        element = $(element);

        element.style.display = '';

        return element;
    };

	/**
	* Method used to both get and set the opacity of the given element
	*
	* @param {Element} element The element to set the opacity for
	* @param {Number} opacity The opacity to set between 1-0
	*
	* @return {Element|Number} Either this element or the opacity of this element
	*/
	Element.prototype.opacity = function( element, opacity )
	{
		element = $(element);

		if( !opacity )
		{
			if( typeof element.style.filter == 'string' )
				return ( element.style.filter.replace(/(.*)opacity=["']?([0-9.]+)["']?(.*)/, "$2")/100  || 1.0  );
			else
				return ( element.style.opacity || 1.0 );
		}

		opacity = ( opacity < 0 ? 0 : ( opacity > 1 ? 1 : opacity) );

		if( typeof element.style.filter == 'string' )
		{
			element.style.zoom = 1;
			element.style.filter = element.style.filter.replace(/alpha\(opacity=['"]?([0-9.]+)['"]?\)/g, "") + " alpha(opacity=" + (opacity * 100) + ")";
		}
		else
		{
			element.style.opacity = opacity;
		}

		return element;
	}

    /**
     *  Method used to get all of the direct children for the given element
     *
     *  @param {Element} element The element to get the children for
     *
     *  @return {Array} An array of all the children elements of this element
     */
	 //Investagte into NodeFilter, TreeWalker, and NodeIterator
    Element.prototype.offspring = function (element) {
		element = $(element);

		//is there a cahced copy of the offspring?
		if ( !! element._offspring)
			return element._offspring;

		var offspring = [];

		var i = -1,
		j = -1,
		childNodes = element.childNodes,
		child;

		if (!childNodes)
			return offspring;

		DragonFly.duff(childNodes.length, function ()
		{
			child = childNodes[++i];

			if ( !!child && !!child.tagName)
				offspring[++j] = $(child);
		});

		//cache and return the result
		return (element._offspring = offspring);
    };

    /**
     * Method used to get all of the descendants of this element
     *
     * @param {Element} element The element to get the descendants for
     *
     * @return {Array} a flattened array of all of the descendants
     */
    Element.prototype.descendants = function (element, tagName)
	{
		//first lets check the cache
		if( !!element._descendants && !tagName )
			return element._descendants;

		//make sure it is a DragonFly Element
		element = $(element);

		var tagName = (typeof tagName == 'string' ? tagName : "*"),
			elements = element.getElementsByTagName(tagName) || [],
			length = elements.length,
			descendants = [];

		for( var i = 0; i < length; ++i )
			descendants[i] = $(elements[i]); //format each of the descendants found and store them into the return array

		//cache and return the descendants
		return ( element._descendants = descendants );
    };

    /**
     *  Method used to get all ancestors of the given element
     *
     * @param {Element} element The element to get the anscestor for
     *
     * @return {Array} a flattened array of all of the ancestors
     */
    Element.prototype.ancestors = function (element) {
        element = $(element);

        var ancestors = [],
        ancestor = element.parent(),
        i = -1;

        while ( !! ancestor) {
	  ancestors[++i] = ancestor;
	  ancestor = ancestor.parent();
        }

        return ancestors;
    };

    /**
     * Method used to get the direct parent of this element
     *
     * @param {Element} element The element to get the parent node for
     *
     * @return {Element|null} The parent node, null if no parent is found
     */
    Element.prototype.parent = function (element) {
        element = $(element);

        var parent = element.parentNode;

        return (!!parent && !!parent.tagName ? $(parent) : null);
    };

    /**
     * Method used to get the siblings of this element
     *
     * @param {Element} element The element to get the siblings for
     *
     * @return {Array} The siblings
     */
    Element.prototype.siblings = function (element) {
        element = $(element);

        return element.parent().offspring().remove(element, 1);
    };

    /**
     * Method used to get the next sibling Element of the given element
     *
     * @param {Element} element The element to get the next sibling for
     *
     * @return {Element|null} The next sibling, if one was found
     */
    Element.prototype.next = function (element) {
		element = $(element);

		var next = element.nextSibling;
		while ( !!next ) {
			if( !!next.tagName )
				return $(next);

			next = next.nextSibling;
		}

		return next;
    };

    /**
     * Method used to get the previous sibling Element of the given element
     *
     * @param {Element} element The element to get the previous sibling for
     *
     * @return {Element|null} The previous sibling, if one was found
     */
    Element.prototype.previous = function (element) {
		element = $(element);

		var previous = element.previousSibling;
		while ( !!previous ) {
			if( !!previous.tagName )
				return $(previous);

			previous = previous.previousSibling;
		}

		return null;
    };

	/**
	* Method used to select (using css) elements within this element
	*
	* @param {Element} element The element to select from
	* @param {String} css The css to search with
	*
	* @return {Array} An array of the elements that were found

	TODO: this isn't working during chaining
	*/
	Element.prototype.select = function( element, css )
	{
		return $$(css, element);
	}

    /**
     * Method used to check if this element has this class name
    *
    * @param {Element} element The element to look at
    * @param {String} className The class name to look for
    *
    * @return {Boolean} Was this class name found?
     */
    Element.prototype.hasClass = function( element, className )
    {
        element = $(element);

        return ( ( " " + element.className.replace(/[\t\n]/g, " ") + " " ).indexOf(" " + className + " ") > -1 );
    };

    /**
     * Method used to remove a given class (if found) from an element
    *
    * @param {Element} element The element whose class needs to be removed
    * @param {String} className The class name to remove from the element
    *
    * @return {Element} Itself
     */
    Element.prototype.addClass = function( element, className )
    {
        element = $(element);

        if( typeof className != 'string' )
	  return element;

        //does this class already exist in the element's class definition?
        element.className += " " + className;

        return element;
    };

    /**
     * Method used to remove a given class (if found) from an element
    *
    * @param {Element} element The element whose class needs to be removed
    * @param {String} className The class name to remove from the element
    *
    * @return {Element} Itself
     */
    Element.prototype.removeClass = function( element, className )
    {
        element = $(element);

        if( typeof className != 'string' )
	  return element;

        //does this class already exist in the element's class definition?
        element.className = (" " + className + " ").replace( new RegExp("[ \t\n]" + className.replace(/[^A-Za-z0-9-_]/g, "") + "[ \t\n]", "g"), " ");

        return element;
    };

    /**
    * @comment Method used to toggle a given class name for an element
    *
    * @param {Element} element The element whose class needs to be toggled
    * @param {String} className The class name to add/remove from the element
    *
    * @return {Element} Itself
    */
    Element.prototype.toggleClass = function( element, className )
    {
        element = $(element);

        if( typeof className != 'string' )
	  return element;

        return ( element.hasClass(className) ? element.removeClass(className) : element.addClass(className) )
    };
})();

//extend the array object to handle element manipulation where elements are found in the array
//TODO: Make this into an element method so that it may be utilized in the future.
(function () {
    Object.prototype.each.call(Element.prototype, function (key, func) {
        //is this property a function?
        if (typeof func === 'function') {
	  //assign the property to the array prototype
	  Array.prototype[key] = function () {
	      //temporary storage
	      var array = [],
	      args = [],
	      i = -1,
	      j = -1,
	      item;

	      //loop over each ov the arguments and append it to the args array
	      DragonFly.duff(arguments.length, function () {
		args[++i] = this[i];
	      }, arguments);

	      //now loop over each element in this array and attempt to call the method
	      //on each individual element found within this array
	      i = -1;
	      DragonFly.duff(this.length, function () {
		item = this[++i];
		if (Object.isElement(item)) array[++j] = item[key].apply(item, args);
	      }, this);

	      //return the updated array, the array is an array of executed methods
	      return array;
	  }
        }
    });
})();

/**
    TODO: Insert Raven Here
**/
 var $$ = function(css, element)
{

}

/**
 * Method used to access a page element and extend its functionaltiy with the
 * Element object's functionlaity
 *
 * @param {String|Element} element The element to find and extend
 *
 * @return {Element} The extended element
 */
var $ = function (element) {
    if (Object.isElement(element)) return element;

    //are we looking for the element in the document?
    if (typeof element == 'string') {
        element = document.getElementById(element);
    }

    //was an element object found?
    if (typeof element !== 'object') throw Error("Invalid argument given for element, " + element);

    //extend the element
    Element.extend(element);

    //return the morified element
    return element;
};